package com.qw.flutter.xlog;

import android.content.Context;
import android.text.TextUtils;

import com.tencent.mars.xlog.Log;
import com.tencent.mars.xlog.Xlog;

/**
 * 创建时间: 2020/7/13 11:52
 * 作者:lixu
 * 功能描述:xlog日志封装
 */
public class XLogWrapper {
    private final static String TAG = "XLogWrapper";

    /**
     * 日志tag前缀
     */
    private static String TAG_PREFIX = "_xxlog：";

    /**
     * 单条日志输出最大长度
     * xlog单条日志控制台最大输出2kb
     * 700*3(汉字最大3字节)=2100字节,保证每条日志都能完整输出在控制台
     * <p>
     * xlog保存日志到文件,单条日志最大限制是16kb,能满足大多数日志的输出
     * 所以仅在debug时才分割日志,显示在控制台
     */
    private static final int MAX_LENGTH = 700;

    /**
     * 日志加密公钥
     * 公钥要与so编译时使用的一致,否则log不能加密
     */
    private static final String LOG_PUB_KEY = "0f8f3869f18feb304a98b5048273f2d20e707ca85" +
            "438ea4311a8aa6f5e3117154fb692eaf3703" +
            "82ed1e04aab3735a244f7405ab5659b3155d95402c345520451";

    private static final XLogWrapper INSTANCE = new XLogWrapper();

    /**
     * 是否已经初始化
     */
    private boolean isInit = false;

    /**
     * 日志库配置参数
     */
    private XLogConfig logConfig;
    private boolean isPrintThreadName;

    public static XLogWrapper getInstance() {
        return INSTANCE;
    }


    private XLogWrapper() {
    }

    /**
     * 初始化
     *
     * @param context
     */
    public synchronized void init(Context context, XLogConfig logConfig) {
        if (!isInit) {
            this.logConfig = logConfig;
            this.isPrintThreadName = logConfig.isPrintThreadName;
            TAG_PREFIX = logConfig.logTag + TAG_PREFIX;

            try {
                //初始化日志组件
                System.loadLibrary("c++_shared");
                System.loadLibrary("marsxlog");

                Xlog.appenderOpen(Xlog.LEVEL_ALL,
                        Xlog.AppednerModeAsync,
                        getCachePath(context),
                        getLogPath(context),
                        logConfig.logFileNamePrefix,
                        0,
                        logConfig.isLogFileEncrypt ? LOG_PUB_KEY : null);

                Xlog.setConsoleLogOpen(logConfig.isConsoleLogOpen);
                Log.setLogImp(new Xlog());
                isInit = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 对长日志进行分割打印
     *
     * @param level
     * @param tag
     * @param log
     */
    public void printLog(String level, String tag, String log) {
        tag = TAG_PREFIX + tag;
        if (isInit && log != null) {
            if (!logConfig.isConsoleLogOpen) {
                //线上环境不需要输出到控制台:不用分割日志直接保存(单条日志最大16kb)
                log(level, tag, log);
                return;
            }

            int length = log.length();
            if (length <= MAX_LENGTH) {
                log(level, tag, log);
            } else {
                try {
                    int start = 0;
                    int end = MAX_LENGTH;

                    while (true) {
                        //剩下的文本还是大于规定长度则继续重复截取并输出
                        if (length > end) {
                            log(level, tag, log.substring(start, end));
                            start = end;
                            end = end + MAX_LENGTH;
                        } else {
                            log(level, tag, log.substring(start, length));
                            break;
                        }
                    }
                } catch (Exception e) {
                    android.util.Log.e(TAG, e.getMessage());
                }
            }
        } else {
            android.util.Log.e(tag + "【日志库初始化前打印的日志，仅显示在控制台】", log);
        }
    }

    /**
     * 调用XLog组件打印日志
     *
     * @param level
     * @param tag
     * @param msg
     */
    private void log(String level, String tag, String msg) {
        if (isPrintThreadName) {
            String threadName = Thread.currentThread().getName() + "-" + Thread.currentThread().getId();
            tag += "[" + threadName + "]";
        }

        if ("e".equalsIgnoreCase(level)) {
            Log.e(tag, msg);
        } else if ("w".equalsIgnoreCase(level)) {
            Log.w(tag, msg);
        } else if ("i".equalsIgnoreCase(level)) {
            Log.i(tag, msg);
        } else if ("v".equalsIgnoreCase(level)) {
            Log.v(tag, msg);
        } else {
            Log.d(tag, msg);
        }
    }

    /**
     * 当日志写入模式为异步时，调用该接口会把内存中的日志写入到文件
     * isSync : true 为同步 flush，flush 结束后才会返回。 false 为异步 flush，不等待 flush 结束就返回
     * 每次登录房间时触发
     */
    public synchronized void appenderFlush() {
        if (isInit) {
            Log.appenderFlush(false);
        }
    }

    public synchronized void onDestroy() {
        if (isInit) {
            Log.appenderFlush(false);
            Log.appenderClose();
            isInit = false;
        }
    }


    /**
     * 日志保存的目录
     * 如果没有获取存储权限,mars会自动存到getLogCachePath()目录
     *
     * @param context
     * @return
     */
    private String getLogPath(Context context) {
        String path = logConfig.logPath;
        if (TextUtils.isEmpty(path)) {
            path = context.getFilesDir().getAbsolutePath() + "xxlog/";
        }
        return path;
    }

    /**
     * 日志缓存目录
     *
     * @param context
     * @return
     */
    private String getCachePath(Context context) {
        return context.getCacheDir() + "/xxlog/";
    }

}
